/* * Copyright © 2013. Palomino Labs (http://palominolabs.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.palominolabs.crm.sf.soap; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.google.common.annotations.VisibleForTesting; import com.palominolabs.crm.sf.core.Id; import com.palominolabs.crm.sf.core.SObject; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.ApiQueryFault; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.Create; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.CreateResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.Delete; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.DeleteResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.DeleteResultType; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.DescribeGlobal; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.DescribeGlobalResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.DescribeGlobalResultType; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.DescribeSObjectResultType; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.DescribeSObjects; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.DescribeSObjectsResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.EmptyRecycleBin; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.EmptyRecycleBinResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.EmptyRecycleBinResultType; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.ExceptionCode; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.GetServerTimestamp; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.GetServerTimestampResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.GetServerTimestampResultType; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.GetUserInfo; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.GetUserInfoResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.InvalidFieldFault_Exception; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.InvalidIdFault_Exception; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.InvalidQueryLocatorFault_Exception; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.InvalidSObjectFault_Exception; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.Logout; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.LogoutResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.MalformedQueryFault_Exception; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.Query; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.QueryAll; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.QueryAllResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.QueryMore; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.QueryMoreResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.QueryResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.QueryResultType; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.Retrieve; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.RetrieveResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.SaveResultType; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.Soap; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.Undelete; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.UndeleteResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.UndeleteResultType; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.UnexpectedErrorFault_Exception; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.Update; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.UpdateResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.Upsert; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.UpsertResponse; import com.palominolabs.crm.sf.soap.jaxwsstub.partner.UpsertResultType; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.slf4j.ext.XLogger; import org.slf4j.ext.XLoggerFactory; import javax.annotation.Nonnull; import javax.annotation.concurrent.ThreadSafe; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.ws.WebServiceException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /* * logger is static but is used inside instance-synchronized methods */ @SuppressWarnings("AccessToStaticFieldLockedOnInstance") @ThreadSafe final class PartnerConnectionImpl extends AbstractSalesforceConnection implements PartnerConnection { private static final XLogger logger = XLoggerFactory.getXLogger(PartnerConnectionImpl.class); private final MetricRegistry metricRegistry; /** * @param semaphore the semaphore to use to limit the number of concurrent calls * @param bundle the ConnectionBundleImpl that this connection is a part of * @param metricRegistry metrics registry to use for api call timing */ private PartnerConnectionImpl(@Nonnull CallSemaphore semaphore, @Nonnull ConnectionBundleImpl bundle, MetricRegistry metricRegistry) { super(semaphore, bundle); this.metricRegistry = metricRegistry; } @Nonnull static PartnerConnectionImpl getNew(@Nonnull CallSemaphore semaphore, @Nonnull ConnectionBundleImpl bundle, MetricRegistry metricRegistry) { return new PartnerConnectionImpl(semaphore, bundle, metricRegistry); } @Override public synchronized int count(@Nonnull String sObjectType, @Nonnull String condition) throws ApiException { String queryStr = "SELECT count() FROM " + sObjectType + " WHERE " + condition; QueryResultType qResultStub = this.queryImpl(queryStr); return qResultStub.getSize(); } @Override public synchronized int count(@Nonnull String sObjectType) throws ApiException { String queryStr = "SELECT count() FROM " + sObjectType; QueryResultType qResultStub = this.queryImpl(queryStr); return qResultStub.getSize(); } @Override public synchronized int countAll(@Nonnull String sObjectType, @Nonnull String condition) throws ApiException { logger.entry(sObjectType, condition); String queryStr = "SELECT count() FROM " + sObjectType + " WHERE " + condition; QueryResultType qResultStub = this.queryAllImpl(queryStr); int size = qResultStub.getSize(); logger.exit(size); return size; } @Override public synchronized int countAll(@Nonnull String sObjectType) throws ApiException { logger.entry(sObjectType); String queryStr = "SELECT count() FROM " + sObjectType; QueryResultType qResultStub = this.queryAllImpl(queryStr); int size = qResultStub.getSize(); logger.exit(size); return size; } @Nonnull @Override public synchronized List<SaveResult> create(@Nonnull List<SObject> sObjects) throws ApiException { logger.entry(sObjects); Create createParam = new Create(); List<com.palominolabs.crm.sf.soap.jaxwsstub.partner.SObject> stubSObjects = createParam.getSObjects(); this.writeSObjectsToStubSObjectList(sObjects, stubSObjects); CreateResponse response; CreateOp op = this.new CreateOp(); response = op.execute(createParam); List<SaveResultType> stubResults = response.getResult(); List<SaveResult> results = new ArrayList<SaveResult>(); for (SaveResultType stubResult : stubResults) { results.add(new SaveResultImpl(stubResult)); } logger.exit(results); return results; } @Nonnull @Override public synchronized List<DeleteResult> delete(@Nonnull List<Id> ids) throws ApiException { logger.entry(ids); Delete deleteParam = new Delete(); for (Id id : ids) { deleteParam.getIds().add(id.toString()); } DeleteResponse response; DeleteOp op = this.new DeleteOp(); response = op.execute(deleteParam); List<DeleteResult> results = new ArrayList<DeleteResult>(); for (DeleteResultType stubResult : response.getResult()) { results.add(new DeleteResult(stubResult)); } logger.exit(results); return results; } @Nonnull @Override public synchronized DescribeGlobalResult describeGlobal() throws ApiException { logger.entry(); DescribeGlobal descrParam = new DescribeGlobal(); DescribeGlobalResponse response; DescribeGlobalOp op = this.new DescribeGlobalOp(); response = op.execute(descrParam); DescribeGlobalResultType stubResult = response.getResult(); DescribeGlobalResult result = new DescribeGlobalResultImpl(stubResult); logger.exit(result); return result; } @Nonnull @Override public synchronized SObjectDescription describeSObject(@Nonnull String sObjectType) throws ApiException { List<String> sObjTypes = new ArrayList<String>(); sObjTypes.add(sObjectType); List<SObjectDescription> results = this.describeSObjects(sObjTypes); if (results.size() != 1) { throw ApiException .getNew("Got back " + results.size() + " results instead of exactly 1", this.getUsername()); } return results.get(0); } @Nonnull @Override public synchronized List<SObjectDescription> describeSObjects(@Nonnull List<String> sObjectTypes) throws ApiException { logger.entry(sObjectTypes); DescribeSObjects descrParam = new DescribeSObjects(); descrParam.getSObjectType().addAll(sObjectTypes); DescribeSObjectsResponse dResponse; DescribeSObjectsOp op = this.new DescribeSObjectsOp(); dResponse = op.execute(descrParam); List<SObjectDescription> descrList = new ArrayList<SObjectDescription>(); for (DescribeSObjectResultType result : dResponse.getResult()) { SObjectDescription descr = new SObjectDescription(result); descrList.add(descr); } logger.exit(descrList); return descrList; } @Nonnull @Override public synchronized List<EmptyRecycleBinResult> emptyRecycleBin(@Nonnull List<Id> ids) throws ApiException { logger.entry(ids); EmptyRecycleBin param = new EmptyRecycleBin(); EmptyRecycleBinResponse response; for (Id id : ids) { param.getIds().add(id.toString()); } EmptyRecycleBinOp op = this.new EmptyRecycleBinOp(); response = op.execute(param); List<EmptyRecycleBinResult> list = new ArrayList<EmptyRecycleBinResult>(); for (EmptyRecycleBinResultType stubResult : response.getResult()) { list.add(new EmptyRecycleBinResult(stubResult)); } logger.exit(list); return list; } @Nonnull @Override public synchronized DateTime getServerTimestamp() throws ApiException { logger.entry(); GetServerTimestamp param = new GetServerTimestamp(); GetServerTimestampResponse response; GetServerTimestampOp op = this.new GetServerTimestampOp(); response = op.execute(param); GetServerTimestampResultType result = response.getResult(); XMLGregorianCalendar serverTime = result.getTimestamp(); DateTime time = ApiUtils.convertSFTimeToDateTime(serverTime); logger.exit(time); return time; } @Nonnull @Override public synchronized UserInfo getUserInfo() throws ApiException { logger.entry(); GetUserInfo param = new GetUserInfo(); GetUserInfoResponse response; GetUserInfoOp op = this.new GetUserInfoOp(); response = op.execute(param); UserInfo result = new UserInfo(response.getResult()); logger.exit(result); return result; } @Nonnull @Override public synchronized PartnerQueryResult query(@Nonnull String queryStr) throws ApiException { return getQueryResultForStub(this.queryImpl(queryStr)); } @Nonnull @Override public synchronized PartnerQueryResult queryAll(@Nonnull String queryStr) throws ApiException { return getQueryResultForStub(this.queryAllImpl(queryStr)); } @Nonnull @Override public synchronized PartnerQueryResult queryMore(@Nonnull PartnerQueryLocator locator) throws ApiException { logger.entry(locator); QueryMore qmParam = new QueryMore(); qmParam.setQueryLocator(locator.getContents()); QueryMoreOp op = this.new QueryMoreOp(); QueryMoreResponse qmResponse = op.execute(qmParam); QueryResultType qmResultStub = qmResponse.getResult(); PartnerQueryResult qResult = getQueryResultForStub(qmResultStub); logger.exit(qResult); return qResult; } @Nonnull @Override public synchronized List<SObject> retrieve(@Nonnull String sObjectType, @Nonnull List<Id> ids, @Nonnull List<String> fieldList) throws ApiException { logger.entry(sObjectType, fieldList, ids); Retrieve retrieveParam = new Retrieve(); retrieveParam.setFieldList(StringUtils.join(fieldList, ",")); retrieveParam.setSObjectType(sObjectType); // suppress warning: order is important @SuppressWarnings("TypeMayBeWeakened") List<String> idStrings = new ArrayList<String>(); for (Id id : ids) { idStrings.add(id.toString()); } retrieveParam.getIds().addAll(idStrings); logger.trace("retrieving fields <{}> for ids <{}>", fieldList, ids); RetrieveOp op = new RetrieveOp(); RetrieveResponse response = op.execute(retrieveParam); List<com.palominolabs.crm.sf.soap.jaxwsstub.partner.SObject> stubSObjects = response.getResult(); List<PartnerSObject> facadeSObjects = getSObjectsFromStubs(stubSObjects); logger.exit(facadeSObjects); return new ArrayList<SObject>(facadeSObjects); } @Nonnull @Override public synchronized Map<Id, SObject> retrieveExtended(@Nonnull String sObjectType, @Nonnull List<Id> ids, @Nonnull List<String> fields, int maxFieldNameChunkSize) throws ApiException { // we don't care about order since we're assuming some Ids may be lost, so use an unordered // map Map<Id, SObject> inProgressMap = new HashMap<Id, SObject>(); for (Id id : ids) { inProgressMap.put(id, PartnerSObjectImpl.getNewWithId(sObjectType, id)); } // create a list of field name chunks List<List<String>> fieldNameChunks = ConnectionUtils.splitFieldList(fields, maxFieldNameChunkSize); // the list of ids being retrieved for each chunk List<Id> idList; for (List<String> fieldListChunk : fieldNameChunks) { // reset the list of ids to whatever is in the map currently idList = new ArrayList<Id>(inProgressMap.keySet()); // actually retrieve the sobjects List<SObject> retrievedSObjects; try { retrievedSObjects = this.retrieve(sObjectType, idList, fieldListChunk); } catch (ApiException e) { throw this.getApiExceptionWithCause("Couldn't retrieve a field name chunk", e); } if (retrievedSObjects.size() != idList.size()) { throw ApiException.getNew("Not all Ids had records retrieved", this.getUsername()); } // see if we did not have access for certain IDs. If you do not have access, the user is // not an admin, or something like that. Iterator<SObject> retrievedSObjIter = retrievedSObjects.iterator(); Iterator<Id> idIter = idList.iterator(); SObject retrievedSObj; Id currentId; while (retrievedSObjIter.hasNext()) { retrievedSObj = retrievedSObjIter.next(); currentId = idIter.next(); // if the sObject is null, remove the id from the map, and remove that sobject from // the list if (retrievedSObj == null) { logger.info("Could not retrieve for id <" + currentId + ">: got back null. Removing from the in progress map. This can mean" + " that field permissions are set wrong or that the fields selected" + " have caused an internal error on Salesforce's side, perhaps because " + "the generated SQL exceeded their Oracle install's max query length."); inProgressMap.remove(currentId); retrievedSObjIter.remove(); } } // at this point, idList is now out of date and should not be used // should not ever be able to get an Id that isn't isn't already in the inProgress map for (SObject retrievedCopy : retrievedSObjects) { SObject inProgressCopy = inProgressMap.get(retrievedCopy.getId()); if (inProgressCopy == null) { throw ApiException .getNew("Somehow got an SObject back from retrieve() with an Id that we did not ask for", this.getUsername()); } // update fields with the latest ones inProgressCopy.setAllFields(retrievedCopy.getAllFields()); } } return inProgressMap; } @Nonnull @Override public synchronized List<UndeleteResult> undelete(@Nonnull List<Id> ids) throws ApiException { logger.entry(ids); Undelete param = new Undelete(); for (Id id : ids) { param.getIds().add(id.toString()); } UndeleteOp op = new UndeleteOp(); UndeleteResponse response = op.execute(param); List<UndeleteResult> results = new ArrayList<UndeleteResult>(); for (UndeleteResultType undeleteResultType : response.getResult()) { results.add(new UndeleteResult(undeleteResultType)); } logger.exit(results); return results; } @Nonnull @Override public synchronized List<SaveResult> update(@Nonnull List<SObject> sObjects) throws ApiException { logger.entry(sObjects); Update param = new Update(); List<com.palominolabs.crm.sf.soap.jaxwsstub.partner.SObject> stubSObjectList = param.getSObjects(); writeSObjectsToStubSObjectList(sObjects, stubSObjectList); UpdateOp op = this.new UpdateOp(); UpdateResponse response = op.execute(param); List<SaveResult> results = new ArrayList<SaveResult>(); for (SaveResultType stubResult : response.getResult()) { results.add(new SaveResultImpl(stubResult)); } logger.exit(results); return results; } @Nonnull @Override public synchronized List<UpsertResult> upsert(@Nonnull String externalIdFieldName, @Nonnull List<SObject> sObjects) throws ApiException { logger.entry(sObjects); Upsert param = new Upsert(); param.setExternalIDFieldName(externalIdFieldName); List<com.palominolabs.crm.sf.soap.jaxwsstub.partner.SObject> stubSObjectList = param.getSObjects(); writeSObjectsToStubSObjectList(sObjects, stubSObjectList); UpsertOp op = this.new UpsertOp(); UpsertResponse upsertResponse = op.execute(param); List<UpsertResult> results = new ArrayList<UpsertResult>(); for (UpsertResultType upsertResultType : upsertResponse.getResult()) { results.add(new UpsertResult(upsertResultType)); } logger.exit(results); return results; } /** * Expose the logout operation so that tests can see what happens with an invalid session id. * * @throws ApiException if logout fails */ @VisibleForTesting synchronized void logout() throws ApiException { final LogoutOp op = new LogoutOp(); op.execute(new Logout()); } private void writeSObjectsToStubSObjectList(List<SObject> sObjects, List<com.palominolabs.crm.sf.soap.jaxwsstub.partner.SObject> stubSObjectList) throws ApiException { for (SObject sObj : sObjects) { try { stubSObjectList.add(SObjects.convertFacadeSObjectToStubSObject(sObj)); } catch (SObjectConversionException e) { throw this.getApiExceptionWithCause("Couldn't convert an sobject to a stub sobject", e); } } } /** * Extract a facade query result from the stub. * * @param qResultStub the stub query result * * @return the facade query result * * @throws ApiException if data cannot be extracted from the stub */ @Nonnull private PartnerQueryResult getQueryResultForStub(@Nonnull QueryResultType qResultStub) throws ApiException { List<PartnerSObject> facadeSObjects = getSObjectsFromStubs(qResultStub.getRecords()); if (qResultStub.isDone()) { return PartnerQueryResultImpl.getDone(facadeSObjects, qResultStub.getSize()); } return PartnerQueryResultImpl .getNotDone(facadeSObjects, qResultStub.getSize(), new PartnerQueryLocator(qResultStub.getQueryLocator())); } /** * @param stubs list of stub sobjects * * @return list of facade sobjects * * @throws ApiException if the facade sobjects cannot be created */ private List<PartnerSObject> getSObjectsFromStubs( List<com.palominolabs.crm.sf.soap.jaxwsstub.partner.SObject> stubs) throws ApiException { try { return SObjects.convertStubListToSObjectList(stubs); } catch (SObjectConversionException e) { // private method need not be synchronized throw ApiException.getNewWithCause("Couldn't extract data from stub SObjects", this.getUsername(), e); } } /** * Do the exception handling on the underlying stub query call since we need it for both the external query() and * count() * * * @param queryStr the SOQL query * @return the stub result type obj * * @throws ApiException on failure */ private QueryResultType queryImpl(String queryStr) throws ApiException { logger.entry(queryStr); Query queryParam = new Query(); queryParam.setQueryString(queryStr); QueryOp op = this.new QueryOp(); QueryResponse qResponse = op.execute(queryParam); QueryResultType result = qResponse.getResult(); logger.exit(result); return result; } /** * Used by queryAll() and countAll() * * * @param queryStr the SOQL query * @return stub result type * * @throws ApiException on failure */ private QueryResultType queryAllImpl(String queryStr) throws ApiException { logger.entry(queryStr); QueryAll queryParam = new QueryAll(); queryParam.setQueryString(queryStr); QueryAllOp op = this.new QueryAllOp(); QueryAllResponse qResponse = op.execute(queryParam); QueryResultType result = qResponse.getResult(); logger.exit(result); return result; } private abstract class PartnerApiOperation<Tin, Tout> extends ApiOperation<Tin, Tout, Soap> { private final Timer timer = metricRegistry.timer(MetricRegistry.name(getClass(), "request")); @Override void releaseBinding(@Nonnull Soap binding) { connBundle.acceptReleasedPartnerBinding(binding); } @Nonnull @Override ConfiguredBinding<Soap> getBinding() throws ApiException { return connBundle.getPartnerBinding(); } @Nonnull @Override Tout executeImpl(@Nonnull ConfiguredBinding<Soap> configuredBinding, @Nonnull Tin param) throws ApiException { try { return executeOpWrapper(configuredBinding, param); } catch (ApiException origApiEx) { if (this.isNotInvalidSessionIdFault(origApiEx)) { logger.warn("Call failed", origApiEx); throw origApiEx; } logger.info( "Detected an INVALID_SESSION_ID fault for user <" + PartnerConnectionImpl.this.getUsername() + ">, attempting to re-log-in", origApiEx); try { connBundle.reportBadSessionId(); } catch (ApiException reconfEx) { logger.warn("Reconfiguration failed", reconfEx); throw ApiException.getNewWithApiExceptionCause("Reconfiguration failed", reconfEx); } // reconfiguration succeeded, try again with a new binding final ConfiguredBinding<Soap> binding2 = getBinding(); logger.info("Reconfiguration succeeded, retrying"); try { return executeOpWrapper(binding2, param); } catch (ApiException retryAttemptApiEx) { logger.warn("Retry after reconfiguration failed; giving up", retryAttemptApiEx); throw retryAttemptApiEx; } finally { releaseBinding(binding2.getBinding()); } } } private Tout executeOpWrapper(ConfiguredBinding<Soap> configuredBinding, Tin param) throws ApiException { Timer.Context context = timer.time(); Tout out; try { // release the permit sooner since getUsername() used by getApiException can also cause an api hit PartnerConnectionImpl.this.acquireSemaphore(); try { out = this.executeOp(configuredBinding.getBinding(), param); } finally { PartnerConnectionImpl.this.releaseSemaphore(); } } catch (InvalidFieldFault_Exception e) { throw this.getApiExceptionWithCauseAndQueryFault("Invalid field", e, e.getFaultInfo()); } catch (InvalidIdFault_Exception e) { throw this.getApiExceptionWithCauseAndFault("Invalid Id", e, e.getFaultInfo()); } catch (InvalidQueryLocatorFault_Exception e) { throw this.getApiExceptionWithCauseAndFault("Invalid query locator", e, e.getFaultInfo()); } catch (InvalidSObjectFault_Exception e) { throw this.getApiExceptionWithCauseAndQueryFault("Invalid SObject", e, e.getFaultInfo()); } catch (MalformedQueryFault_Exception e) { throw this.getApiExceptionWithCauseAndQueryFault("Malformed query", e, e.getFaultInfo()); } catch (UnexpectedErrorFault_Exception e) { throw this.getApiExceptionWithCauseAndFault("Unexpected error", e, e.getFaultInfo()); } catch (WebServiceException e) { throw PartnerConnectionImpl.this.getApiExceptionWithCause("Web Service exception", e); } finally { context.stop(); } return out; } private ApiException getApiExceptionWithCauseAndQueryFault(String message, Throwable cause, ApiQueryFault f) { // field is guarded by synchronization on PartnerConnectionImpl.this return ApiException .getNewWithCauseAndStubApiQueryFault(message, PartnerConnectionImpl.this.getUsername(), cause, f); } /** * Get a ApiException object. It does not check for a INVALID_SESSION_ID fault code. * * @param message the exception message * @param cause the cause of the exception * @param f the stub ApiFault object (not this package's ApiFault) * * @return a call exception object */ @SuppressWarnings("UnnecessaryFullyQualifiedName") private ApiException getApiExceptionWithCauseAndFault(String message, Throwable cause, com.palominolabs.crm.sf.soap.jaxwsstub.partner.ApiFault f) { return ApiException .getNewWithCauseAndStubApiFault(message, PartnerConnectionImpl.this.getUsername(), cause, f); } /** * Check if the ApiFault is caused by an invalid session id. This must be called on every ApiFault that is * created to ensure that an invalid session id is detected. If an invalid session id is detected, the * connection's ConnectionBundle is informed. * * This is only package-visible so that inner classes can use it. Do not call this from outside * PartnerConnection. * * @param f the api fault to check * * @return true if the api fault was an invalid session id */ private boolean isNotInvalidSessionIdFault(ApiException f) { return f.getApiFaultCode() != ExceptionCode.INVALID___SESSION___ID; } /** * Implemented by subclasses to actually do the binding operations * * @param param the binding parameter (the stub parameter, not the externally supplied parameter(s)) * * @return the output of the call to the binding * * @throws InvalidFieldFault_Exception * @throws InvalidIdFault_Exception * @throws InvalidQueryLocatorFault_Exception * * @throws InvalidSObjectFault_Exception * @throws MalformedQueryFault_Exception * @throws UnexpectedErrorFault_Exception */ abstract Tout executeOp(@Nonnull Soap binding, @Nonnull Tin param) throws InvalidFieldFault_Exception, InvalidIdFault_Exception, InvalidQueryLocatorFault_Exception, InvalidSObjectFault_Exception, MalformedQueryFault_Exception, UnexpectedErrorFault_Exception; } private class CreateOp extends PartnerApiOperation<Create, CreateResponse> { @Override CreateResponse executeOp(@Nonnull Soap binding, @Nonnull Create param) throws InvalidFieldFault_Exception, InvalidIdFault_Exception, InvalidSObjectFault_Exception, UnexpectedErrorFault_Exception { return binding.create(param); } } private class DeleteOp extends PartnerApiOperation<Delete, DeleteResponse> { @Override DeleteResponse executeOp(@Nonnull Soap binding, @Nonnull Delete param) throws UnexpectedErrorFault_Exception { return binding.delete(param); } } private class DescribeGlobalOp extends PartnerApiOperation<DescribeGlobal, DescribeGlobalResponse> { @Override DescribeGlobalResponse executeOp(@Nonnull Soap binding, @Nonnull DescribeGlobal param) throws UnexpectedErrorFault_Exception { return binding.describeGlobal(param); } } private class DescribeSObjectsOp extends PartnerApiOperation<DescribeSObjects, DescribeSObjectsResponse> { @Override DescribeSObjectsResponse executeOp(@Nonnull Soap binding, @Nonnull DescribeSObjects param) throws InvalidSObjectFault_Exception, UnexpectedErrorFault_Exception { return binding.describeSObjects(param); } } private class EmptyRecycleBinOp extends PartnerApiOperation<EmptyRecycleBin, EmptyRecycleBinResponse> { @Override EmptyRecycleBinResponse executeOp(@Nonnull Soap binding, @Nonnull EmptyRecycleBin param) throws UnexpectedErrorFault_Exception { return binding.emptyRecycleBin(param); } } private class GetServerTimestampOp extends PartnerApiOperation<GetServerTimestamp, GetServerTimestampResponse> { @Override GetServerTimestampResponse executeOp(@Nonnull Soap binding, @Nonnull GetServerTimestamp param) throws UnexpectedErrorFault_Exception { return binding.getServerTimestamp(param); } } private class GetUserInfoOp extends PartnerApiOperation<GetUserInfo, GetUserInfoResponse> { @Override GetUserInfoResponse executeOp(@Nonnull Soap binding, @Nonnull GetUserInfo param) throws UnexpectedErrorFault_Exception { return binding.getUserInfo(param); } } private class QueryAllOp extends PartnerApiOperation<QueryAll, QueryAllResponse> { @Override QueryAllResponse executeOp(@Nonnull Soap binding, @Nonnull QueryAll param) throws InvalidFieldFault_Exception, InvalidIdFault_Exception, InvalidQueryLocatorFault_Exception, InvalidSObjectFault_Exception, MalformedQueryFault_Exception, UnexpectedErrorFault_Exception { return binding.queryAll(param); } } private class QueryMoreOp extends PartnerApiOperation<QueryMore, QueryMoreResponse> { @Override QueryMoreResponse executeOp(@Nonnull Soap binding, @Nonnull QueryMore param) throws InvalidFieldFault_Exception, InvalidQueryLocatorFault_Exception, UnexpectedErrorFault_Exception, MalformedQueryFault_Exception { return binding.queryMore(param); } } private class QueryOp extends PartnerApiOperation<Query, QueryResponse> { @Override QueryResponse executeOp(@Nonnull Soap binding, @Nonnull Query param) throws InvalidFieldFault_Exception, InvalidIdFault_Exception, InvalidQueryLocatorFault_Exception, InvalidSObjectFault_Exception, MalformedQueryFault_Exception, UnexpectedErrorFault_Exception { return binding.query(param); } } private class RetrieveOp extends PartnerApiOperation<Retrieve, RetrieveResponse> { @Override RetrieveResponse executeOp(@Nonnull Soap binding, @Nonnull Retrieve param) throws InvalidFieldFault_Exception, InvalidIdFault_Exception, InvalidSObjectFault_Exception, MalformedQueryFault_Exception, UnexpectedErrorFault_Exception { return binding.retrieve(param); } } private class UpdateOp extends PartnerApiOperation<Update, UpdateResponse> { @Override UpdateResponse executeOp(@Nonnull Soap binding, @Nonnull Update param) throws InvalidFieldFault_Exception, InvalidIdFault_Exception, InvalidSObjectFault_Exception, UnexpectedErrorFault_Exception { return binding.update(param); } } private class UpsertOp extends PartnerApiOperation<Upsert, UpsertResponse> { @Override UpsertResponse executeOp(@Nonnull Soap binding, @Nonnull Upsert param) throws InvalidFieldFault_Exception, InvalidIdFault_Exception, InvalidQueryLocatorFault_Exception, InvalidSObjectFault_Exception, MalformedQueryFault_Exception, UnexpectedErrorFault_Exception { return binding.upsert(param); } } private class LogoutOp extends PartnerApiOperation<Logout, LogoutResponse> { @Override LogoutResponse executeOp(@Nonnull Soap binding, @Nonnull Logout param) throws InvalidFieldFault_Exception, InvalidIdFault_Exception, InvalidQueryLocatorFault_Exception, InvalidSObjectFault_Exception, MalformedQueryFault_Exception, UnexpectedErrorFault_Exception { return binding.logout(param); } } private class UndeleteOp extends PartnerApiOperation<Undelete, UndeleteResponse> { @Override UndeleteResponse executeOp(@Nonnull Soap binding, @Nonnull Undelete param) throws InvalidFieldFault_Exception, InvalidIdFault_Exception, InvalidQueryLocatorFault_Exception, InvalidSObjectFault_Exception, MalformedQueryFault_Exception, UnexpectedErrorFault_Exception { return binding.undelete(param); } } }